﻿using Sustainalytics.LongRunningTaskManager.Interface;
using Sustainalytics.Utils;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;

namespace Sustainalytics.LongRunningTaskManager
{
    public class LongRunningTaskFactory : ILongRunningTaskFactory
    {
        // dictionary of associations between types and task types
        private static readonly ConcurrentDictionary<LongRunningTaskTypeEnum, Type> _taskTypes;


        /// <summary>
        /// Static ctor.
        /// </summary>
        static LongRunningTaskFactory()
        {
            // dictionary of types
            _taskTypes = new ConcurrentDictionary<LongRunningTaskTypeEnum, Type>();

            // load all types
            LoadTypes();
        }

        /// <summary>
        /// Load all types.
        /// </summary>
        private static void LoadTypes()
        {
            // get all types that are tasks
            IEnumerable<Type> types = AssemblyUtility.GetTypes<ILongRunningTask>().Where(t => t.IsClass && t.IsAbstract == false);

            // foreach type create an instance to discover member values
            foreach (Type type in types)
            {
                ILongRunningTask task;

                // create instance
                try
                {
                    task = (ILongRunningTask)Activator.CreateInstance(type);

                    // add type to dictionary
                    _taskTypes.TryAdd(task.Type, type);

                    Debug.WriteLine(string.Format("LRTM :: Task Added. [{0}]", type.FullName));

                    // log action
                    LongRunningTaskManagerEventSource.Log.TaskManagerFactoryTaskAdded(type.FullName);
                }
                catch (Exception ex)
                {
                    Debug.WriteLine(string.Format("LRTM :: Task Add ERROR. [{0}]", type.FullName));

                    // log error
                    LongRunningTaskManagerEventSource.Log.TaskManagerFactoryTaskAddError(type.FullName, ex);
                }                
            }
        }


        /// <summary>
        /// Get all registered task types.
        /// </summary>
        /// <returns> All registered task types. </returns>
        public IEnumerable<Type> GetLongRunningTaskTypes()
        {
            var types = _taskTypes.ToList().Select(el => el.Value).ToList();
            return types;
        }

        /// <summary>
        /// Get all types associated to a specific task type.
        /// </summary>
        /// <param name="taskType"> Task type. </param>
        /// <returns> Type associated to a specific task type. </returns>
        public Type GetLongRunningTaskType(LongRunningTaskTypeEnum taskType)
        {
            Type type;

            if (_taskTypes.TryGetValue(taskType, out type) == false)
            {
                throw new LongRunningTaskTypeNotSupportedException(taskType);
            }

            return type;
        }

        /// <summary>
        /// Create task for a specific task type.
        /// </summary>
        /// <param name="taskType"> Task type. </param>
        /// <returns> Task. </returns>
        public ILongRunningTask CreateLongRunningTask(LongRunningTaskTypeEnum taskType)
        {
            // get type associated to a specific task type
            Type type = GetLongRunningTaskType(taskType);

            // create task
            ILongRunningTask task = (ILongRunningTask)Activator.CreateInstance(type);

            return task;
        }
    }
}
